package com.wcy.app.lib.crash

import android.app.Application
import android.content.ActivityNotFoundException
import android.content.Context
import android.content.pm.PackageManager
import android.os.Build
import android.os.Looper
import android.os.Process
import android.text.TextUtils
import android.view.WindowManager.BadTokenException
import android.widget.Toast
import androidx.annotation.NonNull
import androidx.work.Data
import androidx.work.OneTimeWorkRequest
import androidx.work.WorkManager
import com.hjq.permissions.Permission
import com.hjq.permissions.XXPermissions
import java.io.PrintWriter
import java.io.StringWriter
import java.text.SimpleDateFormat
import java.util.*


class CrashHandler : Thread.UncaughtExceptionHandler {
    companion object {
        private val INSTANCE = CrashHandler()

        /**
         * 注册 Crash 监听
         */
        fun getInstance(): CrashHandler {
            return INSTANCE
        }

    }

    private var mApplication: Application? = null
    private var mOldHandler: Thread.UncaughtExceptionHandler? = null

    fun init(application: Application) {
        mApplication = application
        mOldHandler = Thread.getDefaultUncaughtExceptionHandler()
        Thread.setDefaultUncaughtExceptionHandler(this)
        check(javaClass.name != mOldHandler!!.javaClass.name) {
            // 请不要重复注册 Crash 监听
            "are you ok?"
        }
    }

    override fun uncaughtException(@NonNull thread: Thread?, @NonNull throwable: Throwable?) {
        object : Thread() {
            override fun run() {
                // Toast 显示需要出现在一个线程的消息队列中
                Looper.prepare()
                Toast.makeText(mApplication, "程序异常，请重新打开", Toast.LENGTH_LONG)
                        .show()
                Looper.loop()
            }
        }.start()
        // Sleep一会后结束程序
        // 来让线程停止一会是为了显示Toast信息给用户，然后Kill程序
        try {
            if (mApplication != null) {
                val putData = Data.Builder().putString("msg", initData(throwable!!)).build()
                val uploadrecordworker = OneTimeWorkRequest.Builder(UploadCatchUncaughtWorker::class.java).setInputData(putData).build()
                WorkManager.getInstance(mApplication!!).enqueue(uploadrecordworker)
            }
            Thread.sleep(3000)
        } catch (e: InterruptedException) {
        }
        // 杀死进程（这个事应该是系统干的，但是它会多弹出一个崩溃对话框，所以需要我们自己手动杀死进程）
        Process.killProcess(Process.myPid())
        System.exit(10)
    }

    private fun initData(throwable: Throwable): String? {
        val stringWriter = StringWriter()
        val printWriter = PrintWriter(stringWriter)
        throwable.printStackTrace(printWriter)
        val builder = StringBuilder()
        if (throwable is NullPointerException) {
            builder.append("空指针异常")
        } else if (throwable is ClassCastException) {
            builder.append("类型转换异常")
        } else if (throwable is ActivityNotFoundException) {
            builder.append("活动跳转异常")
        } else if (throwable is IllegalArgumentException) {
            builder.append("非法参数异常")
        } else if (throwable is IllegalStateException) {
            builder.append("非法状态异常")
        } else if (throwable is BadTokenException) {
            builder.append("窗口添加异常")
        } else if (throwable is StackOverflowError) {
            builder.append("栈溢出")
        } else if (throwable is OutOfMemoryError) {
            builder.append("内存溢出")
        }
        builder.append("\n设备品牌：").append(Build.BRAND)
                .append("\t设备型号：").append(Build.MODEL)
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            builder.append("\n安卓版本：").append(Build.VERSION.RELEASE)
                    .append("\tSDK\t版本：").append(Build.VERSION.SDK_INT)
                    .append("\tCPU\t架构：").append(Build.SUPPORTED_ABIS[0])
        }
        builder.append("\n版本代码：\t").append(getAppVersionName(mApplication!!))
        try {
            val packageInfo = mApplication!!.packageManager.getPackageInfo(mApplication!!.packageName, PackageManager.GET_PERMISSIONS)
            val DATE_FORMAT = SimpleDateFormat("YY-MM-dd HH:mm")
            builder.append("\n首次安装：\t").append(DATE_FORMAT.format(Date(packageInfo.firstInstallTime)))
                    .append("\t最近安装：\t").append(DATE_FORMAT.format(Date(packageInfo.lastUpdateTime)))
                    .append("\t崩溃时间：\t").append(DATE_FORMAT.format(Date()))
            val permissions = packageInfo.requestedPermissions
            if (permissions.contains(Permission.MANAGE_EXTERNAL_STORAGE) || permissions.contains(Permission.MANAGE_EXTERNAL_STORAGE)) {
                builder.append("\n存储权限：\t")
                if (XXPermissions.hasPermission(mApplication, Permission.MANAGE_EXTERNAL_STORAGE)) {
                    builder.append("读、写")
                } else {
                    if (XXPermissions.hasPermission(mApplication, Permission.MANAGE_EXTERNAL_STORAGE)) {
                        builder.append("读")
                    } else if (XXPermissions.hasPermission(mApplication, Permission.MANAGE_EXTERNAL_STORAGE)) {
                        builder.append("写")
                    } else {
                        builder.append("未获得")
                    }
                }
            }
            if (permissions.contains(Permission.CAMERA)) {
                builder.append("\t相机权限：\t").append(if (XXPermissions.hasPermission(mApplication, Permission.CAMERA)) "已获得" else "未获得")
            }
            if (permissions.contains(Permission.REQUEST_INSTALL_PACKAGES)) {
                builder.append("\t安装包权限：\t").append(if (XXPermissions.hasPermission(mApplication, Permission.REQUEST_INSTALL_PACKAGES)) "已获得" else "未获得")
            }
        } catch (ignored: PackageManager.NameNotFoundException) {
        }
        builder.append("\n" + stringWriter.toString());
        return builder.toString()
    }

    private fun getAppVersionName(context: Context): String? {
        val packageName: String = context.packageName
        return if (TextUtils.isEmpty(packageName)) {
            null
        } else {
            try {
                val pm: PackageManager = context.getPackageManager()
                val pi = pm.getPackageInfo(packageName, 0)
                pi?.versionName
            } catch (var4: PackageManager.NameNotFoundException) {
                var4.printStackTrace()
                null
            }
        }
    }
}